package xyz.hzxiao.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * 网络请求工具类
 *
 * @author ：FuXx
 * @className : xyz.hzxiao.utils.NetRequest
 * @since ：2023/3/7
 */
public class NetRequest {

    private static final Logger LOGGER = LoggerFactory.getLogger(NetRequest.class);

    public static NetRequestBuilder builder() {
        LOGGER.info("|----------------------------------NET-START---------------------------------|");
        return new NetRequestBuilder();
    }

    public static class NetRequestBuilder {

        private final OkHttpClient CLIENT;

        private static final MediaType JSON_SUBMIT = MediaType.get("application/json;charset=UTF-8");

        private static final MediaType FORM_SUBMIT = MediaType.get("application/x-www-form-urlencoded;charset=UTF-8");

        private String responseString;

        /**
         * get
         *
         * @param url 请求地址
         * @throws IOException 网络错误
         */
        public NetRequestBuilder get(String url) throws IOException {
            Request request = new Request.Builder()
                    .url(url)
                    .build();
            return this.sendRequest(request);
        }

        /**
         * 带参数查询
         *
         * @param url   请求地址
         * @param param 请求参数
         * @throws IOException 网络错误
         */
        public NetRequestBuilder get(String url, Map<String, Object> param) throws IOException {
            HttpUrl.Builder builder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder();
            for (Map.Entry<String, Object> entry : param.entrySet()) {
                builder.addQueryParameter(entry.getKey(), entry.getValue().toString());
            }
            String requestParam = builder.build().toString();
            LOGGER.info("|-请求参数：{}", requestParam);
            Request request = new Request.Builder()
                    .url(requestParam)
                    .build();
            return this.sendRequest(request);
        }

        /**
         * @param url       请求地址
         * @param param     json参数
         * @param mediaType 媒体类型
         * @throws IOException 网络错误
         */
        public NetRequestBuilder post(String url, Map<String, Object> param, MediaType mediaType) throws IOException {
            RequestBody body = initRequestBody(param, mediaType);
            Request request = new Request.Builder()
                    .url(url)
                    .post(body)
                    .build();
            return this.sendRequest(request);
        }

        /**
         * @param url 请求地址
         * @throws IOException 网络错误
         */
        public NetRequestBuilder uploadFile(String url, RequestBody requestBody) throws IOException {
            Request request = new Request.Builder()
                    .url(url)
                    .post(requestBody)
                    .build();
            return this.sendRequest(request);
        }

        /**
         * @param url       请求地址
         * @param param     json参数
         * @param mediaType 媒体类型
         * @throws IOException 网络错误
         */
        public NetRequestBuilder put(String url, Map<String, Object> param, MediaType mediaType) throws IOException {
            RequestBody body = initRequestBody(param, mediaType);
            Request request = new Request.Builder()
                    .url(url)
                    .put(body)
                    .build();
            return this.sendRequest(request);
        }

        /**
         * @param url   请求地址
         * @param param 请求参数
         * @throws IOException 网络错误
         */
        public NetRequestBuilder putForm(String url, Map<String, Object> param) throws IOException {
            return this.put(url, param, FORM_SUBMIT);
        }

        /**
         * @param url   请求地址
         * @param param 请求参数
         * @throws IOException 网络错误
         */
        public NetRequestBuilder postForm(String url, Map<String, Object> param) throws IOException {
            return this.post(url, param, FORM_SUBMIT);
        }

        /**
         * @param url   请求地址
         * @param param 请求参数
         * @throws IOException 网络错误
         */
        public NetRequestBuilder postJson(String url, Map<String, Object> param) throws IOException {
            return this.post(url, param, JSON_SUBMIT);
        }

        /**
         * @param url   请求地址
         * @param param 请求参数
         * @throws IOException 网络错误
         */
        public NetRequestBuilder putJson(String url, Map<String, Object> param) throws IOException {
            return this.put(url, param, JSON_SUBMIT);
        }

        /**
         * 获取响应的字符串
         */
        public String responseAtString() {
            return this.responseString;
        }

        /**
         * 响应的字符串数据转对象
         *
         * @param clazz 目标类
         */
        public <T> Optional<T> responseAtObject(Class<T> clazz) {
            JSONObject result = JSON.parseObject(this.responseString);
            return Optional.ofNullable(JSON.parseObject(result.getString("data"), clazz));
        }

        private NetRequestBuilder sendRequest(Request request) throws IOException {
            LOGGER.info("|-请求开始, 地址： {}", request.url());
            try (Response response = CLIENT.newCall(request).execute()) {
                if (response.isSuccessful()) {
                    LOGGER.info("|-请求完成，本次耗时：{} 毫秒", response.receivedResponseAtMillis() - response.sentRequestAtMillis());
                    LOGGER.info("|----------------------------------NET-STOP---------------------------------|");
                    this.responseString = new String(response.body().bytes(), StandardCharsets.UTF_8);
                    return this;
                }
                LOGGER.error("请求失败：{}，消息：{}", response.message(), new String(response.body().bytes(), StandardCharsets.UTF_8));
                throw new IOException("网络错误");
            }
        }

        private RequestBody initRequestBody(Map<String, Object> param, MediaType mediaType) {
            String requestParam = JSON.toJSONString(param);
            RequestBody body;
            if (FORM_SUBMIT.equals(mediaType)) {
                FormBody.Builder builder = new FormBody.Builder();
                for (Map.Entry<String, Object> entry : param.entrySet()) {
                    builder.add(entry.getKey(), entry.getValue().toString());
                }
                body = builder.build();
            } else {
                body = RequestBody.create(mediaType, requestParam);
            }
            LOGGER.info("|-请求参数：{}", param);
            return body;
        }

        public NetRequestBuilder() {
            this.CLIENT = new OkHttpClient()
                    .newBuilder()
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .readTimeout(10, TimeUnit.SECONDS)
                    .writeTimeout(10, TimeUnit.SECONDS)
                    .build();
        }
    }
}
